#include "gtest/gtest.h"
#include <filesystem>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#ifndef SOURCE_DIR
#define SOURCE_DIR "."
#endif

std::string e2e_dir = std::string(SOURCE_DIR) + "/tests/e2e";

namespace fs = std::filesystem;

// Helper: Run a command and capture stdout
std::string run_and_capture(const std::string& cmd) {
    std::string result;
    FILE* pipe = popen(cmd.c_str(), "r");
    if (!pipe) return "";
    char buffer[128];
    while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
        result += buffer;
    }
    pclose(pipe);
    return result;
}

// Helper: Get all test base names in tests/e2e/
std::vector<std::string> get_test_bases() {
    std::vector<std::string> bases;
    std::string e2e_dir = std::string(SOURCE_DIR) + "/tests/e2e";
    if (!fs::exists(e2e_dir)) {
        std::cerr << "E2E test directory does not exist: " << e2e_dir << std::endl;
        return bases;
    }
    for (const auto& entry : fs::directory_iterator(e2e_dir)) {
        if (entry.path().extension() == ".decaf") {
            std::string base = entry.path().stem();
            std::string txt = e2e_dir + "/" + base + ".txt";
            if (fs::exists(txt)) {
                bases.push_back(base);
            }
        }
    }
    return bases;
}


class DecafE2ETest : public ::testing::TestWithParam<std::string> {};

TEST_P(DecafE2ETest, OutputMatches) {
    std::string base = GetParam();
    std::string decaf = std::string(SOURCE_DIR) + "/tests/e2e/" + base + ".decaf";
    std::string txt   = std::string(SOURCE_DIR) + "/tests/e2e/" + base + ".txt";
    std::string ll    = std::string(SOURCE_DIR) + "/tests/e2e/" + base + ".ll";
    std::string bin   = std::string(SOURCE_DIR) + "/tests/e2e/" + base + ".bin";


    std::string compiler_path = std::string(SOURCE_DIR) + "/build/src/DecafLangCompiler";
    // Compile to native binary
    std::string compile_cmd = compiler_path + " build " + decaf + " QUIET";
    ASSERT_EQ(std::system(compile_cmd.c_str()), 0);



    // Run the binary and capture output
    std::string output = run_and_capture(bin);

    // Read expected output
    std::ifstream expected_file(txt);
    std::stringstream expected_buf;
    expected_buf << expected_file.rdbuf();
    std::string expected = expected_buf.str();

    // Remove trailing newlines for comparison
    while (!output.empty() && (output.back() == '\n' || output.back() == '\r')) output.pop_back();
    while (!expected.empty() && (expected.back() == '\n' || expected.back() == '\r')) expected.pop_back();

    EXPECT_EQ(output, expected);

    std::remove(ll.c_str());
    std::remove(bin.c_str());
}

// Auto-discover all test cases
INSTANTIATE_TEST_SUITE_P(
    DecafE2E,
    DecafE2ETest,
    ::testing::ValuesIn(get_test_bases())
);
